package mj.predictor;
 
import java.util.ArrayList;
 
/**
 *
 * @author dirkkoh
 */
public class MahJongProbability {
        /********
         * This is the object that to store the details of analysis.
         * X1Prob means the probability that the opponent hold one of the specific card.
         * X2Prob means the probability that the opponent hold two of the specific card and so on.
         */
        private int MJID;
        private String MJDesc;
        private double X1Prob;
        private double X2Prob;
        private double X3Prob;
        private double X4Prob;
       
        public MahJongProbability(int mJID, ArrayList<MahJong> mJSet, String opponent) {
                /**********
                 * The logic here to calculate the probability is based on the total of unknown cards(TotalUnknown),
                 * the specific unknown card(SingleMJUnknown) and the opponent unknown card(OpponentUnknown).
                 * To get more details from this 3 types of arguments, see the comments besides the declaration as below;
                 */
                int TotalUnknown = 0; //The total of unknown cards that without "Flower" type, including the cards of pool and the cards that opponents hold
                int SingleMJUnknown = 0; //The total specific cards that remains unknown. Max number is 4.
                int OpponentUnknown = 13; //The total of opponent card that remains unknown. Max number is 13. The number will only get less when opponent "pong" or "eat"
                double Probability = 0;
               
                this.MJID = mJID; //Assign the specific card ID
               
                /**********
                 * First stage is to loop all the unknown card to trace down the value of those 3 arguments that stated above.
                 * Store the total number of status "unknown" cards that without "Flower" Type in TotalUnknown.
                 * Store the total number of status "unknown card that with specific card ID in SingleMJUnknown.
                 * If find out any card is not "unknown" and under status of opponent, deduct the value of OpponentUnknown.
                 */
                for (int i = 0; i < mJSet.size(); i++) {
                        if (mJSet.get(i).GetMJStatus() == "Unknown" && mJSet.get(i).GetMJType() != "Flower") {
                                TotalUnknown += 1;
                                if (mJSet.get(i).GetMJID() == mJID) {
                                        SingleMJUnknown += 1;
                                        this.MJDesc = mJSet.get(i).GetMJDesc(); //Assign the specific card description
                                }
                        }
                        else if (mJSet.get(i).GetMJStatus() == opponent) {
                                OpponentUnknown -= 1;
                        }
                }
                /***********
                 * Second stage is to calculate the probability that the opponent hold a single specific card.
                 * Firstly check the value of SingleMJUnknown, if the value is 0 means there is no "unknown" card for this specific cards,
                 * the total 4 cards had been exposed, so probability will equal to 0.
                 * The system only can calculate the probability where SingleMJUnknown is between 1 and 4. Below is the logic of algorithm.
                 * Given "TotalUnknown" unknown card, opponent hold "OpponentUnknown" of card,
                 * what are the probabilities of the opponent hold the 1 specific card, 2 specific cards...4 specific cards respectively?
                 * Set S be the total ways that opponent could hold the cards,
                 * hence we get n(S) = nCr; n = TotalUnknown * (TotalUnknown - 1), r = OpponentUnknown * (OpponentUnknown - 1)
                 * n(S) = (TotalUnknown * (TotalUnknown - 1)) / (OpponentUnknown * (OpponentUnknown - 1))
                 * Set E be the total ways to get one specific card out of "SingleMJUnknown"
                 * hence we get n(E) = nCr; n = SingleMJUnknown * (SingleMJUnknown - 1), r = 1
                 * n(E) = (SingleMJUnknown * (SingleMJUnknown - 1)) / 1
                 * Probability of the event p(E) = n(E)/n(S),
                 * therefore we get [(SingleMJUnknown * (SingleMJUnknown - 1)) * (OpponentUnknown * (OpponentUnknown - 1))] / ((double)TotalUnknown * ((double)TotalUnknown - 1))  
                 * However, in order to write this logic into programming language, we need to take care a couple of situations that stated below
                 * mainly to avoid the probability become 0 when SingleMJUnknown = 1 or OpponentUnknown = 1
                 */
                if (SingleMJUnknown != 0) {
                        if (TotalUnknown > 1 && SingleMJUnknown > 1 && OpponentUnknown > 1) { //normal situation
                                Probability = (((double)SingleMJUnknown * ((double)SingleMJUnknown - 1)) * ((double)OpponentUnknown * ((double)OpponentUnknown - 1))) /
                                                ((double)TotalUnknown * ((double)TotalUnknown - 1));
                        }
                       
                        else if (TotalUnknown > 1 && SingleMJUnknown == 1 && OpponentUnknown > 1) { //When SingleMJUnknown = 1, to avoid (double)SingleMJUnknown * ((double)SingleMJUnknown - 1) = 0 then get the probability equal to 0
                                Probability = (((double)SingleMJUnknown) * ((double)OpponentUnknown * ((double)OpponentUnknown - 1))) /
                                                ((double)TotalUnknown * ((double)TotalUnknown - 1));
                        }
                       
                        else if (TotalUnknown > 1 && SingleMJUnknown == 1 && OpponentUnknown == 1) { //When both SingleMJUnknown and OpponentUnknown equal to 1, OpponentUnknown equal to 1 means "DanDiao"
                                Probability = (((double)SingleMJUnknown) * ((double)OpponentUnknown * ((double)OpponentUnknown - 1))) /
                                                ((double)TotalUnknown);
                        }
                       
                        else if (TotalUnknown > 1 && SingleMJUnknown > 1 && OpponentUnknown == 1) { //When OpponentUnknown = 1
                                Probability = (((double)SingleMJUnknown * ((double)SingleMJUnknown - 1)) * ((double)OpponentUnknown * ((double)OpponentUnknown - 1))) /
                                                ((double)TotalUnknown);
                        }
                }
                /**********
                 * From the second stage we get the probability of specific one card that opponent might hold.
                 * In the third stage is about to calculate the probability that opponent hold more than one specific card.
                 * The logic basically is same as the second stage until the step to calculate n(E)
                 * Set E be the total ways to get two, three or four out of "SingleMJUnknown"
                 * hence we get n(E) = nCr;  n = SingleMJUnknown * (SingleMJUnknown - 1), r = (2 * (2 - 1)) or (3 * (3 - 1)) or (4 * (4 - 1))
                 * n(E) = (SingleMJUnknown * (SingleMJUnknown - 1)) / r
                 * Probability of the event p(e) = n(E)/n(S) = p(E)/ r
                 * Therefore we get the algorithm as below
                 */
                if (SingleMJUnknown == 4) { //if SingleMJUnknown equals to 4, the opponent might have chances to hold all of 4 of the cards
                        this.X1Prob = Probability;
                        this.X2Prob = (double)Probability / 2;
                        this.X3Prob = (double)Probability / 6;
                        this.X4Prob = (double)Probability / 12;
                }
               
                else if (SingleMJUnknown == 3) { //if SingleMJUnknown equals to 3, the opponent is only possible to hold max 3 of the cards
                        this.X1Prob = Probability;
                        this.X2Prob = (double)Probability / 2;
                        this.X3Prob = (double)Probability / 6;
                        this.X4Prob = 0.00;
                }
               
                else if (SingleMJUnknown == 2) { //Same logic
                        this.X1Prob = Probability;
                        this.X2Prob = (double)Probability / 2;
                        this.X3Prob = 0.00;
                        this.X4Prob = 0.00;
                }
               
                else if (SingleMJUnknown == 1) { //Same logic
                        this.X1Prob = Probability;
                        this.X2Prob = 0.00;
                        this.X3Prob = 0.00;
                        this.X4Prob = 0.00;
                }
               
                else { //if SingleMJUnknown = 0, opponent have no chance to hold the card
                        this.X1Prob = 0.00;
                        this.X2Prob = 0.00;
                        this.X3Prob = 0.00;
                        this.X4Prob = 0.00;
                }
        }
       
        public String GetMJDesc() {
                return this.MJDesc;
        }
       
        public double GetX1Prob() {
                return this.X1Prob;
        }
       
        public double GetX2Prob() {
                return this.X2Prob;
        }
       
        public double GetX3Prob() {
                return this.X3Prob;
        }
       
        public double GetX4Prob() {
                return this.X4Prob;
        }
 
}